#include "SmartObjects.h"
#include "../Math/MathCode.h"

/****
This section added by Steve
****/
float EntropyGenerator(void)
{
	float Retval = (float) (rand() % 1000) / 5000.f;
	return Retval + 0.2f;
}

float GenericStimRecOperation(const Receptor &Rec, const Stimulus &Stim, float maxRecVal)
{
  float stim = Stim.weight;
  float stim2 = stim * stim;
  float recep = Rec.currentState;
  float numerator = -2*recep*stim - stim*stim;
  float denominator = maxRecVal * maxRecVal;

  if( denominator ) {
    float final = numerator / denominator;
    return final;
  }
	return 0.f;
}

float GenericRecOperation( const Receptor &Rec)
{
	//return ( Rec.currentState >= 0.f ? Rec.currentState : -Rec.currentState );
	//return Rec.currentState;
	return 0.f;
}

float GenericStimOperation(const Stimulus &Stim)
{
	//return ( Stim.weight >= 0.f ? Stim.weight : -Stim.weight );
	//return Stim.weight;
	return 0.f;
}


/*******************************************************************************
  Static Variables
*******************************************************************************/
AttributeIdGenerator gSmartObjectIDGenerator;

/*******************************************************************************
  Stimulus Code
*******************************************************************************/
Stimulus::Stimulus() : attributeID(0), weight(0),
                       stimulusEvaluation(0) {
}

bool 
Stimulus::operator()( const Stimulus& a, const Stimulus& b ) {
  if( a.attributeID < b.attributeID )
    return true;
  return false;
}

/*******************************************************************************
  Receptor Code
*******************************************************************************/
Receptor::Receptor() : attributeID(0), currentState(0),
                       receptorEvaluation(0), pairEvaluation(0) {
}

bool 
Receptor::operator()( const Receptor& a, const Receptor& b ) {
  if( a.attributeID < b.attributeID )
    return true;
  return false;
}

/*******************************************************************************
  Smart Object Code
*******************************************************************************/
SmartObject::SmartObject() : name("Default Object") {
}

SmartObject::SmartObject( std::string name ) : name(name){
}

void 
SmartObject::AddStimulus( std::string name, float weight, 
                          Stimulus::StimFunc stimFunc ) {
  Stimulus::ID id = gSmartObjectIDGenerator.GetId( name );
  AddStimulus( id, weight, stimFunc );
}

void 
SmartObject::AddStimulus( Stimulus::ID id, float weight, 
                          Stimulus::StimFunc stimFunc ) {
  Stimulus temp;
  temp.attributeID = id;
  temp.weight = weight;
  temp.stimulusEvaluation = stimFunc;

  stimulants.insert( temp );
}

SmartObject::StimIter 
SmartObject::ChangeStimulus( std::string name, float weight, 
                             Stimulus::StimFunc stimFunc ) {
  Stimulus::ID id = gSmartObjectIDGenerator.GetId( name );
  return ChangeStimulus( id, weight, stimFunc );
}

SmartObject::StimIter 
SmartObject::ChangeStimulus( Stimulus::ID id, float weight, 
                             Stimulus::StimFunc stimFunc ) {
  Stimulus temp;
  temp.attributeID = id;
  StimIter iter = stimulants.find( temp );
  if( iter != stimulants.end() ) {
    stimulants.erase( iter );
    AddStimulus( id, weight, stimFunc );
    return stimulants.find( temp );
  }
  
  return stimulants.end();
}

void 
SmartObject::RemoveStimulus( std::string name ) {
  Stimulus::ID id = gSmartObjectIDGenerator.GetId( name );
  RemoveStimulus( id );
}

void 
SmartObject::RemoveStimulus( Stimulus::ID id ) {
  Stimulus temp;
  temp.attributeID = id;

  StimIter iter = stimulants.find( temp );
  if( iter != stimulants.end() ) {
    stimulants.erase( iter );
  }
}

void 
SmartObject::SetName( std::string newName ){
  name = newName;
}

const std::string& 
SmartObject::GetName() {
  return name;
}

const SmartObject::StimSet& 
SmartObject::GetStimulants() {
  return stimulants;
}

/*******************************************************************************
  Smart Agent Code
*******************************************************************************/
SmartAgent::SmartAgent() : name("Default Agent") {
}

SmartAgent::SmartAgent( std::string name ) : name(name) {
}
    
void 
SmartAgent::AddReceptor( std::string name, float initState,
                         Receptor::RecepStimFunc pairFunc,
                         Receptor::RecepFunc recepFunc ) {
  Receptor::ID id = gSmartObjectIDGenerator.GetId( name );
  AddReceptor( id, initState, pairFunc, recepFunc );
}

void 
SmartAgent::AddReceptor( Receptor::ID id, float initState,
                         Receptor::RecepStimFunc pairFunc,
                         Receptor::RecepFunc recepFunc ) {
  Receptor temp;
  temp.attributeID = id;
  temp.currentState = initState;
  temp.pairEvaluation = pairFunc;
  temp.receptorEvaluation = recepFunc;
  

  if( ABS( temp.currentState ) > maxReceptor ) {
    maxReceptor = ABS( temp.currentState );
  }

  receptors.insert( temp );
}

SmartAgent::RecepIter 
SmartAgent::ChangeReceptor( std::string name, float initState,
                            Receptor::RecepStimFunc pairFunc,
                            Receptor::RecepFunc recepFunc ) {
  Receptor::ID id = gSmartObjectIDGenerator.GetId( name );
  return ChangeReceptor( id, initState, pairFunc, recepFunc );
}

SmartAgent::RecepIter 
SmartAgent::ChangeReceptor( Receptor::ID id, float initState,
                            Receptor::RecepStimFunc pairFunc,
                            Receptor::RecepFunc recepFunc ) {
  Receptor temp;
  temp.attributeID = id;
 
  RecepIter iter = receptors.find( temp );
  if( iter != receptors.end() ) {
    receptors.erase( iter );
    AddReceptor( id, initState, pairFunc, recepFunc );
    
    return receptors.find( temp );
  }
  
  return receptors.end();
}

void
SmartAgent::RemoveReceptor( std::string name ) {
  Receptor::ID id = gSmartObjectIDGenerator.GetId( name );
  RemoveReceptor( id );
}

void 
SmartAgent::RemoveReceptor( Receptor::ID id ) {
  Receptor temp;
  temp.attributeID = id;

  FindNewMaxReceptor();

  RecepIter iter = receptors.find( temp );
  if( iter != receptors.end() ) {
    receptors.erase( iter );
  }
}

float 
SmartAgent::EvaluateObject( SmartObject& smartObject, int& pairCount ){
  float evaluation = 0;
  pairCount = 0;
  
  // Set up iterators
  SmartObject::StimIter stimIter = smartObject.GetStimulants().begin();
  SmartObject::StimIter stimEnd = smartObject.GetStimulants().end();

  SmartAgent::RecepIter recepIter = receptors.begin();
  SmartAgent::RecepIter recepEnd = receptors.end();

  // Remove stimulants and receptors with invalid ID
  while( stimIter != stimEnd && stimIter->attributeID == 0 ) { ++stimIter; }
  while( recepIter != recepEnd && recepIter->attributeID == 0 ) { ++recepIter; }

  // Loop through and match up stimulus and receptor IDs
  while( stimIter != stimEnd && recepIter != recepEnd ) {
    // While stimulus ID is less than receptor ID
    while( stimIter != stimEnd &&
           stimIter->attributeID < recepIter->attributeID ) {
      if( stimIter->stimulusEvaluation )
        evaluation += stimIter->stimulusEvaluation( *stimIter );
      ++stimIter;
    }
    // End of stimulants was reached
    if( stimIter == stimEnd ) {
      break;
    }
    
    // While receptor ID is less than stimulus ID
    while( recepIter != recepEnd &&
           recepIter->attributeID < stimIter->attributeID ) {
      if( recepIter->receptorEvaluation )
        evaluation += recepIter->receptorEvaluation( *recepIter );
      ++recepIter;
    }
    // End of receptors was reached
    if( recepIter == recepEnd ) {
      break;
    }
    
    // While ID's match
    while( stimIter != stimEnd && recepIter != recepEnd &&
           stimIter->attributeID == recepIter->attributeID ) {
      if( recepIter->pairEvaluation )
        evaluation += recepIter->pairEvaluation( *recepIter, *stimIter, GetMaxReceptor() );
      ++pairCount;
      ++stimIter;
      ++recepIter;
    }
  }

  // Loop through remaining stimulants and receptors
  while( stimIter != stimEnd ) {
    if( stimIter->stimulusEvaluation )
      evaluation += stimIter->stimulusEvaluation( *stimIter );
    ++stimIter;
  }
  while( recepIter != recepEnd ) {
    if( recepIter->receptorEvaluation )
      evaluation += recepIter->receptorEvaluation( *recepIter );
    ++recepIter;
  }

  return evaluation;
}

void 
SmartAgent::UpdateWithObject( SmartObject& smartObject ) {
  SmartObject::StimIter stimIter = smartObject.GetStimulants().begin();
  SmartObject::StimIter stimEnd = smartObject.GetStimulants().end();

  SmartAgent::RecepIter recepIter = receptors.begin();
  SmartAgent::RecepIter recepEnd = receptors.end();
  
  while( stimIter != stimEnd && recepIter != recepEnd ) {
    // Loop until stimulant ID is greater or equal to the receptor ID
    while( stimIter != stimEnd && 
           stimIter->attributeID < recepIter->attributeID ) {
      ++stimIter;
    }
    // End of stimulants was reached
    if( stimIter == stimEnd ) {
      break;
    }
    
    // Loop until receptor ID is greater or equal to the stimulant ID
    while( recepIter != recepEnd &&
           recepIter->attributeID < stimIter->attributeID ) {
      ++recepIter;
    }
    // End of receptors was reached
    if( recepIter == recepEnd ) {
      break;
    }
    
    while( stimIter != stimEnd && recepIter != recepEnd &&
           stimIter->attributeID == recepIter->attributeID ) {
      // Change the receptor value by its corresponding stimulant
      // Reset the receptor end because the set was affected by ChangeReceptor
      recepIter = ChangeReceptor( recepIter->attributeID, 
                                  recepIter->currentState + stimIter->weight,
                                  recepIter->pairEvaluation, 
                                  recepIter->receptorEvaluation );

      recepEnd = receptors.end();
      
      // Increment if not at the end
      if( recepIter != recepEnd ) {
        ++stimIter;
      }
      ++recepIter;
    }
  }

  FindNewMaxReceptor();
}

void 
SmartAgent::SetName( std::string newName ){
  name = newName;
}

const std::string& 
SmartAgent::GetName() {
  return name;
}

const SmartAgent::RecepSet& 
SmartAgent::GetReceptors() {
  return receptors;
}

float SmartAgent::GetMaxReceptor() const {
  return maxReceptor;
}

void SmartAgent::FindNewMaxReceptor() {
  SmartAgent::RecepIter iter = receptors.begin();
  SmartAgent::RecepIter end = receptors.end();

  maxReceptor = 0;
  while( iter != end ) {
    if( ABS(iter->currentState) > maxReceptor ) {
      maxReceptor = ABS( iter->currentState );
    }
    ++iter;
  }
}

/*******************************************************************************
  Attribute Generation Code
*******************************************************************************/
AttributeIdGenerator::AttributeIdGenerator() : currentNewId(1) {
}

AttributeIdGenerator::ID 
AttributeIdGenerator::GetId( std::string name ){
  StrIdIter iter = attributeToIdMap.find( name );
  if( iter != attributeToIdMap.end() ) {
    return iter->second;
  }
  else {
    StrIdInsert it = attributeToIdMap.insert( StrIdPair( name, currentNewId ) );
    if( !it.second ) {
      return 0;
    }
    ++currentNewId;
    return currentNewId - 1;
  }
}

std::string 
AttributeIdGenerator::GetName( ID id ) {
  StrIdIter iter = attributeToIdMap.begin();
  StrIdIter end = attributeToIdMap.end();

  while( iter != end ) {
    if( iter->second == id ) {
      return iter->first;
    }
    ++iter;
  }

  return "<ERROR>";
}
